extern crate cairo;
extern crate gio;
extern crate glib;
extern crate gtk;

use std::cell::RefCell;
use std::rc::Rc;
use std::sync::Arc;

use cairo::Context;
use gio::prelude::*;
use glib::clone;
use gtk::prelude::*;
use gtk::{ApplicationWindow, DrawingArea};

use tsp::map::MapNode;
use tsp::population::Population;

use tsp_viewer::image::Image;
use tsp_viewer::*;

const WIDTH: i32 = 1500;
const HEIGHT: i32 = 1000;

fn build_ui(application: &gtk::Application, matches: clap::ArgMatches<'_>) {
    let window = ApplicationWindow::new(application);
    let area = DrawingArea::new();
    let get_val = |x| matches.value_of(x).unwrap();

    tsp::init_log(&get_val("debug")).unwrap();

    area.set_size_request(WIDTH, HEIGHT);

    let image = RefCell::new(draw_initial());
    let origin = (0, 0);

    let (ready_tx, ready_rx) = glib::MainContext::channel(glib::PRIORITY_DEFAULT);
    let (send_start, recv_start) = std::sync::mpsc::channel();

    let gen_button = |x| {
        let button = gtk::Button::with_label(&format!("进行 {} 轮迭代", x));
        let send_start = send_start.clone();
        button.connect_clicked(move |_| {
            let _ = send_start.send(x);
        });
        button
    };

    let label = gtk::Label::new(Some("当前尚未开始迭代"));
    let fixed = gtk::Fixed::new();

    fixed.put(&area, 0, 0);
    fixed.put(&label, 10, 30);
    let button_number = [1, 10, 100];
    for (i, number) in button_number.iter().enumerate() {
        fixed.put(&gen_button(*number), 10, 60 + (40 * i as i32));
    }
    window.add(&fixed);

    // Read map & gen population
    let mut population = Population::new(
        tsp::read_map(get_val("data")).unwrap(),
        get_val("pn").parse::<u32>().unwrap(),
        get_val("pm").parse::<f64>().unwrap(),
        get_val("pc").parse::<f64>().unwrap(),
    );
    std::thread::spawn(move || {
        let mut cnt = 0;
        for (_, mut times) in recv_start.iter().enumerate() {
            while times > 0 {
                times -= 1;
                cnt += 1;

                let mut image = draw_initial();
                let current_best = population.iterator().unwrap();
                let path = current_best.get_path().get_raw();

                let get_draw_node = |x: &MapNode| -> Node { (x.get_x() * 10., x.get_y() * 10.) };

                for node in path {
                    image.with_surface(|surface| {
                        let cr = Context::new(surface);
                        draw_square(&cr, &get_draw_node(&node), 1., 1.);
                        surface.flush();
                    });
                }

                let connect =
                    |image: &mut Image, last_node: &Arc<MapNode>, cur_node: &Arc<MapNode>| {
                        image.with_surface(|surface| {
                            let cr = Context::new(surface);
                            draw_line(&cr, get_draw_node(&last_node), get_draw_node(&cur_node));
                            surface.flush();
                        });
                    };
                for i in 1..path.len() {
                    connect(&mut image, &path[i - 1], &path[i]);
                }
                connect(&mut image, &path[0], &path[path.len() - 1]);

                let _ = ready_tx.send(GtkUpdate::UpdateImage(image.clone()));
                let label_string =
                    format!("当前是 第 {} 次迭代 长度为 {}", cnt, current_best.fitness());
                let _ = ready_tx.send(GtkUpdate::UpdateLabel(label_string));

                log::info!("distance = {}", current_best.fitness());
            }
        }
    });

    let workspace = Rc::new((image, origin));

    area.connect_draw(
        clone!(@weak workspace => @default-return Inhibit(false), move |_, cr| {
            let (ref image, ref origin) = *workspace;

            image.borrow_mut().with_surface(|surface| {
                draw_image_if_dirty(&cr, surface, *origin, (WIDTH, HEIGHT));
            });

            Inhibit(false)
        }),
    );

    ready_rx.attach(None, move |gtkupdate| {
        match gtkupdate {
            GtkUpdate::UpdateImage(image) => {
                let (ref raw_image, ref origin) = *workspace;

                raw_image.replace(image);
                area.queue_draw_area(origin.0, origin.1, WIDTH, HEIGHT);
            }
            GtkUpdate::UpdateLabel(msg) => {
                label.set_text(msg.as_str());
            }
        };
        glib::Continue(true)
    });

    window.show_all();
}

fn main() {
    let application =
        gtk::Application::new(Some("com.gitee.yingziyu.tsp_viewer"), Default::default())
            .expect("Initialization failed...");

    application.connect_activate(|app| {
        build_ui(app, init_matches());
    });

    application.run(&[]);
}
